package mc.support.orm.shard.sql.comm;

import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import mc.support.orm.shard.ShardException;
import mc.support.orm.shard.ShardStrategy;
import mc.support.orm.shard.sql.DeleteSqlPhaser;
import mc.support.orm.shard.sql.InsertSqlPhaser;
import mc.support.orm.shard.sql.SelectSqlPhaser;
import mc.support.orm.shard.sql.UpdateSqlPhaser;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * Created with IntelliJ IDEA. <br/>
 * Author: Francis Yuen    <br/>
 * Date: 2014-10-15  <br/>
 */
public class GeneralSqlPhaser {

    private static final Log log = LogFactory.getLog( GeneralSqlPhaser.class );

    private final SelectSqlPhaser selectSqlPhaser = new SelectSqlPhaser();
    private final InsertSqlPhaser insertSqlPhaser = new InsertSqlPhaser();
    private final UpdateSqlPhaser updateSqlPhaser = new UpdateSqlPhaser();
    private final DeleteSqlPhaser deleteSqlPhaser = new DeleteSqlPhaser();

    private final Map<Integer, SqlPhaser> cache = new HashMap<>();

    private SqlPhaser resolveSqlPhaser( SQLStatement statement, Integer mapperHash ) {
        SqlPhaser sqlPhaser = cache.get( mapperHash );
        if ( null == sqlPhaser ) {
            synchronized ( cache ) {
                sqlPhaser = cache.get( mapperHash );
                if ( null == sqlPhaser ) {
                    if ( statement instanceof SQLSelectStatement ) {
                        sqlPhaser = selectSqlPhaser;
                    } else if ( statement instanceof SQLInsertStatement ) {
                        sqlPhaser = insertSqlPhaser;
                    } else if ( statement instanceof SQLUpdateStatement ) {
                        sqlPhaser = updateSqlPhaser;
                    } else if ( statement instanceof SQLDeleteStatement ) {
                        sqlPhaser = deleteSqlPhaser;
                    } else {
                        throw new ShardException( "statement type is not supported now, detail type: " +
                                statement.getClass().getName() );
                    }
                    cache.put( mapperHash, sqlPhaser );
                }
            }
        }
        return sqlPhaser;
    }

    public String phase( String sql, Object params, String mapperId, Integer mapperHash, ShardStrategy shardStrategy ) {
        SQLStatement statement;
        try {
            statement = new MySqlStatementParser(sql).parseStatement();
        } catch ( Exception e ) {
            String msg = e.getMessage();
            log.error( msg, e );
            throw new ShardException( msg, e );
        }
        return resolveSqlPhaser( statement, mapperHash ).phase( statement, params, mapperId, shardStrategy );
    }

}
